/**
 * 可拖拽/缩放的对话框
 */
import React, {Component} from 'react';
import ReactDOM from "react-dom";
import { Icon } from "antd";
import "styles/components/drag-dialog.scss"

class DragDialog extends Component {

  state = {
    moveFlag: false, // 拖拽标识
    zoomFlag: false, // 缩放标识
    mouseStartX: 0, // 鼠标mouseDown时的横坐标（相对于对话框）
    mouseStartY: 0, // 鼠标mouseDown时的纵坐标（相对于对话框）
    zoomPlacement: undefined, // 鼠标缩放位置
  };

  componentDidMount() {
    const { name } = this.props;
    const localXY = JSON.parse(window.localStorage.getItem(name)|| '0');
    if (name && localXY) {
      this.contentRef.style.width = localXY.width;
      this.contentRef.style.height = localXY.height;
      this.divRef.style.left = localXY.left;
      this.divRef.style.top = localXY.top;
    }

    if (window.addEventListener) {
      window.addEventListener('mousemove', this.handleMouseMove, true);
      window.addEventListener('mouseup', this.handleMouseUp, true);
    } else {
      window.attachEvent('onmousemove', this.handleMouseMove, true);
      window.attachEvent('onmouseup', this.handleMouseUp, true);
    }
  }

  componentWillUnmount() {
    if (window.addEventListener) {
      window.removeEventListener('mousemove', this.handleMouseMove);
      window.removeEventListener('mouseup', this.handleMouseUp);
    } else {
      window.detachEvent('onmousemove', this.handleMouseMove);
      window.detachEvent('onmouseup', this.handleMouseUp);
    }
  }

  // 获取元素的横坐标（相对于窗口）
  getLeft = e => {
    let offset = e.offsetLeft;
    if (e.offsetParent) offset += this.getLeft(e.offsetParent);
    return offset;
  };

  // 获取元素的纵坐标（相对于窗口）
  getTop = e => {
    let offset = e.offsetTop;
    if (e.offsetParent) offset += this.getTop(e.offsetParent);
    return offset;
  };

  handleMouseDown = (e, type, placement) => {
    e = e || window.event;
    this.setState({
      moveFlag: type === 'move',
      zoomFlag: type === 'zoom',
      mouseStartX: e.clientX - this.getLeft(this.headerRef),
      mouseStartY: e.clientY - this.getTop(this.headerRef),
      zoomPlacement: placement
    });
  };

  handleMouseMove = e => {
    const { minWidth, minHeight } = this.props;
    const { moveFlag, zoomFlag, mouseStartX, mouseStartY, zoomPlacement } = this.state;
    e = e || window.event;

    const setMinValue0 = value => {
      return value > 0 ? value : 0
    };

    // 拖拽
    if (moveFlag) {
      // 当前对话框相对于窗口的坐标
      const nowX = e.clientX - mouseStartX;
      const nowY = e.clientY - mouseStartY;
      // 当前对话框与之前对话框的坐标差
      const disX = nowX - this.getLeft(this.headerRef);
      const disY = nowY - this.getTop(this.headerRef);
      const bottom = window.innerHeight - this.divRef.offsetHeight - disY - this.divRef.offsetTop;
      const right = window.innerWidth - this.divRef.offsetWidth - disX - this.divRef.offsetLeft;

      right > 0 && (this.divRef.style.left = setMinValue0(this.divRef.offsetLeft + disX) + 'px');
      bottom > 0 && (this.divRef.style.top = setMinValue0(this.divRef.offsetTop + disY) + 'px');
    }

    // 缩放
    if (zoomFlag) {
      const disX = e.clientX - this.getLeft(this.contentRef);
      const disY = e.clientY - this.getTop(this.contentRef) + 48;
      let maxHeight = this.divRef.offsetTop + this.divRef.offsetHeight - 48;
      let maxWidth = this.divRef.offsetWidth + this.divRef.offsetLeft;
      let height = this.contentRef.offsetHeight - disY;
      let width  = this.contentRef.offsetWidth - disX;
      switch (zoomPlacement) {
        case 'topLeft':
          if (height > minHeight && height < maxHeight) {
            this.divRef.style.top = this.divRef.offsetTop + disY + 'px';
            this.contentRef.style.height = this.contentRef.offsetHeight - disY + 'px';
          }
          if (height > minWidth && height < maxWidth) {
            this.divRef.style.left = this.divRef.offsetLeft + disX + 'px';
            this.contentRef.style.width = this.contentRef.offsetWidth - disX + 'px';
          }
          break;
        case 'topRight':
          width = disX;
          maxWidth = window.innerWidth - this.divRef.offsetLeft;
          if (height > minHeight && height < maxHeight) {
            this.divRef.style.top = this.divRef.offsetTop + disY + 'px';
            this.contentRef.style.height = this.contentRef.offsetHeight - disY + 'px';
          }
          width > minWidth && width < maxWidth && (this.contentRef.style.width = width+ 'px');
          break;
        case 'bottomLeft':
          maxHeight = window.innerHeight - this.divRef.offsetTop;
          height = disY;
          if (width > minWidth && width < maxWidth) {
            this.divRef.style.left = this.divRef.offsetLeft + disX + 'px';
            this.contentRef.style.width = this.contentRef.offsetWidth - disX + 'px';
          }
          height > minHeight && height < maxHeight && (this.contentRef.style.height = height+ 'px');
          break;
        case 'bottomRight':
          width = disX;
          height = disY;
          maxHeight = window.innerHeight - this.divRef.offsetTop;
          maxWidth = window.innerWidth - this.divRef.offsetLeft;
          width > minWidth && width < maxWidth && (this.contentRef.style.width = width + 'px');
          height > minHeight && height < maxHeight && (this.contentRef.style.height = height + 'px');
          break;
      }
    }
  };

  handleMouseUp = () => {
    this.setState({
      moveFlag: false,
      zoomFlag: false
    });

    this.props.name && this.contentRef && window.localStorage.setItem(this.props.name,
      JSON.stringify({
        width: this.contentRef.style.width,
        height: this.contentRef.style.height,
        left: this.divRef.style.left,
        top: this.divRef.style.top
      }));
  };

  handleClose = () => {
    this.divRef.classList.add('fadeOutLeft');
    this.props.afterClose();
    setTimeout(() => {
      this.props.onClose();
    }, 500);
  };

  renderZoomDiv = placement => (
    <div
      className={`drag-dialog-zoom drag-dialog-zoom-${placement}`}
      onMouseDown={e => this.handleMouseDown(e, 'zoom', placement)}
    >
    </div>
  );

  render() {
    const { visible, title, content, className, minWidth, minHeight, style } = this.props;
    return visible ? (
      <div
        className={`${className} drag-dialog animated fadeInLeft`}
        style={style}
        ref={node => this.divRef = node}
      >
        <div
          ref={node => this.headerRef = node}
          className="drag-dialog-header"
          onMouseDown={e => this.handleMouseDown(e, 'move')}
        >
          {title}
          <Icon type="close" className="drag-dialog-close" onClick={this.handleClose}/>
        </div>
        <div
          className="drag-dialog-content"
          ref={node => this.contentRef = node}
          style={{ minWidth, minHeight }}
        >
          {content}
        </div>
        {this.renderZoomDiv('topLeft')}
        {this.renderZoomDiv('topRight')}
        {this.renderZoomDiv('bottomLeft')}
        {this.renderZoomDiv('bottomRight')}
      </div>
    ) : null;
  }
}

DragDialog.propTypes = {
  visible: React.PropTypes.bool,
  title: React.PropTypes.any,
  content: React.PropTypes.any,
  className: React.PropTypes.string,
  minWidth: React.PropTypes.number, // 内容区域最小宽度
  minHeight: React.PropTypes.number, // 内容区域最小高度
  afterClose: React.PropTypes.func,
};

DragDialog.defaultProps = {
  className: '',
  minWidth: 390,
  minHeight: 195,
  afterClose: () => {}
};

DragDialog.open = function (config = {}, env) {
  let defaultConfig = {
    visible: false,
    className: '',
    onClose: close
  };
  let targetConfig = Object.assign(
    {},
    defaultConfig,
    config,
  );
  let div = document.createElement('div');
  let container = document.body;
  container.appendChild(div);
  function render(props) {
    if (env) {
      ReactDOM.unstable_renderSubtreeIntoContainer(env, <DragDialog {...props}/>, div)
    } else {
      ReactDOM.render(<DragDialog {...props}/>, div)
    }
  }
  function close() {
    if (div) {
      ReactDOM.unmountComponentAtNode(div);
      container.removeChild(div);
      div = null;
    }
  }
  function update(updateConfig) {
    targetConfig = {
      ...targetConfig,
      ...updateConfig
    };
    render(targetConfig);
  }
  render(targetConfig);
  return {
    destroy: close,
    update
  }
};

export default DragDialog;
